﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace Furion.CMS.Core
{
    /// <summary>
    /// 对象操作扩展
    /// </summary>
    public static class ObjectExtensions
    {
        /// <summary>
        /// 转换对象值为Guid
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static Guid ToTryGuid(this object obj)
        {
            if (obj == null) return Guid.Empty;
            Guid.TryParse(obj.ToString(), out var guid);
            return guid;
        }

        /// <summary>
        /// 将字符串转换为指定模型
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="str"></param>
        /// <returns></returns>
        public static TResult ToModel<TResult>(this string str)
        {
            return JsonConvert.DeserializeObject<TResult>(str);
        }

        /// <summary>
        /// 判断是否是自定义类型
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static bool IsCustomType(this Type type)
        {
            return (type != typeof(object) && Type.GetTypeCode(type) == TypeCode.Object);
        }

        /// <summary>
        /// 判断是否是集合类型
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static bool IsEnumerableType(this Type type)
        {
            return (type.GetInterface("IEnumerable") != null);
        }

        /// <summary>
        /// 比较对象并返回差异
        /// </summary>
        /// <param name="oldObj"></param>
        /// <param name="newObj"></param>
        /// <param name="modelType"></param>
        /// <returns></returns>
        public static (string OldResult, string NewResult) ToCompare(this object oldObj, object newObj, Type modelType)
        {
            var oldChangeValue = new Dictionary<string, string>();
            var newChangeValue = new Dictionary<string, string>();

            var props = modelType.GetProperties();
            foreach (var prop in props)
            {
                var propDisplayName = prop.GetCustomAttributes(typeof(DisplayNameAttribute), true)
                    .Cast<DisplayNameAttribute>()
                    .FirstOrDefault()
                    ?.DisplayName;

                object oldValue = prop.GetValue(oldObj);
                object newValue = prop.GetValue(newObj);

                if (prop.PropertyType.IsEnumerableType())
                {
                    var oldStr = oldValue?.ToJson() ?? "";
                    var newStr = newValue?.ToJson();
                    if (!oldStr.Equals(newStr))
                    {
                        // 清除未改变的数据
                        var oldIgnoreMembers = props.Where(x => !oldChangeValue.Keys.Contains(x.Name) && x.Name != prop.Name)
                            .Select(s => s.Name).ToArray();
                        var newIgnoreMembers = props.Where(x => !newChangeValue.Keys.Contains(x.Name) && x.Name != prop.Name)
                            .Select(s => s.Name).ToArray();
                        oldChangeValue = new Dictionary<string, string>
                        {
                            {
                                prop.Name,
                                JsonConvert.SerializeObject(oldObj, new JsonSerializerSettings
                                {
                                    ContractResolver = new CustomDataContractResolver(oldIgnoreMembers),
                                })
                            }
                        };
                        newChangeValue = new Dictionary<string, string>
                        {
                            {
                                prop.Name,
                                JsonConvert.SerializeObject(newObj, new JsonSerializerSettings
                                {
                                    ContractResolver = new CustomDataContractResolver(newIgnoreMembers),
                                })
                            }
                        };
                        break;
                    }
                }
                else if (prop.PropertyType.IsCustomType())
                {
                    var result = oldValue.ToCompare(newValue, prop.PropertyType);
                    oldChangeValue.Add(prop.Name, $"{propDisplayName} {result.OldResult}");
                    newChangeValue.Add(prop.Name, $"{propDisplayName} {result.NewResult}");
                }
                else
                {
                    if (!oldValue.Equals(newValue))
                    {
                        oldChangeValue.Add(prop.Name, $"{propDisplayName} {oldValue}");
                        newChangeValue.Add(prop.Name, $"{propDisplayName} {newValue}");
                    }
                }
            }

            return (string.Join("，", oldChangeValue.Values), string.Join("，", newChangeValue.Values));
        }

        /// <summary>
        /// 比较对象并返回差异
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="oldObj"></param>
        /// <param name="newObj"></param>
        /// <returns></returns>
        public static (string OldResult, string NewResult) ToCompare<T>(this object oldObj, object newObj)
            => ToCompare(oldObj, newObj, typeof(T));

        /// <summary>
        /// 将对象转换为字符串
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string ToJson(this object obj)
        {
            return JsonConvert.SerializeObject(obj);
        }

        /// <summary>
        /// 分割字符串,并移除空白项
        /// </summary>
        /// <param name="str"></param>
        /// <param name="separator"></param>
        /// <returns></returns>
        public static string[] ToSplit(this string str, string separator)
        {
            return str.Split(new string[] { separator }, StringSplitOptions.RemoveEmptyEntries);
        }

        #region encryption and decryption
        /// <summary>
        /// 基于Sha1的自定义加密字符串方法
        /// <para>
        /// 输入一个字符串，返回一个由40个字符组成的十六进制的哈希散列（字符串）。
        /// </para>
        /// </summary>
        /// <param name="str">要加密的字符串</param>
        /// <param name="isLower">是否小写</param>
        /// <returns>加密后的十六进制的哈希散列（字符串）</returns>
        public static string ToSHA1(this string str, bool isLower = false)
        {
            var buffer = Encoding.UTF8.GetBytes(str);
            var data = SHA1.Create().ComputeHash(buffer);

            StringBuilder sub = new StringBuilder();
            foreach (var t in data)
            {
                sub.Append(t.ToString(isLower ? "x2" : "X2"));
            }
            return sub.ToString();
        }

        /// <summary>
        /// 基于HMACSHA1自定义加密字符串方法
        /// <para>
        /// 输入一个字符串，返回一个由40个字符组成的十六进制的哈希散列（字符串）。
        /// </para>
        /// </summary>
        /// <param name="str"></param>
        /// <param name="key"></param>
        /// <param name="isLower"></param>
        /// <returns>加密后的十六进制的哈希散列（字符串）</returns>
        public static string ToHMAC(this string str, string key, bool isLower = false)
        {
            HMACSHA1 myHMACSHA1 = new HMACSHA1(Encoding.UTF8.GetBytes(key));
            byte[] byteText = myHMACSHA1.ComputeHash(Encoding.UTF8.GetBytes(str));
            StringBuilder sub = new StringBuilder();
            foreach (var t in byteText)
            {
                sub.Append(t.ToString(isLower ? "x2" : "X2"));
            }
            return sub.ToString();
        }

        /// <summary>
        /// 基于HMACSHA1自定义加密字符串方法
        /// <para>
        /// 输入一个字符串，返回一个Base64字符串
        /// </para>
        /// </summary>
        /// <param name="str"></param>
        /// <param name="key"></param>
        /// <returns>加密后的Base64字符串</returns>
        public static string ToBase64HMAC(this string str, string key)
        {
            HMACSHA1 myHMACSHA1 = new HMACSHA1(Encoding.UTF8.GetBytes(key));

            byte[] byteText = myHMACSHA1.ComputeHash(Encoding.UTF8.GetBytes(str));

            return System.Convert.ToBase64String(byteText);
        }
        #endregion
        
        /// <summary>
        /// 时间戳格式转换
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static long ToLong(this DateTime dt)
        {
            DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
            TimeSpan toNow = dt.Subtract(dtStart);
            long timeStamp = toNow.Ticks;
            timeStamp = long.Parse(timeStamp.ToString().Substring(0, timeStamp.ToString().Length - 4));
            return timeStamp;
        }

        /// <summary>
        /// 时间戳格式转换
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static long ToLong(this DateTimeOffset dt)
        {
            DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
            TimeSpan toNow = dt.Subtract(dtStart);
            long timeStamp = toNow.Ticks;
            timeStamp = long.Parse(timeStamp.ToString().Substring(0, timeStamp.ToString().Length - 4));
            return timeStamp;
        }
    }
}
